import router from "@/router/router";
import { tokenStore } from "@/stores/tokenStore";
import { ElLoading, ElMessage, ElMessageBox } from "element-plus";
import _ from "lodash";
import { HttpType, TipsType, UploadType } from "../TsEnums";
import { AjaxOption, ResultModel, TipsOpts, TokenModel } from "../Types";
import ApiCore from "./ApiCore";

//全局记录是否正在刷新token
let isRefreshToken: boolean = false;

//记录在报401时有多少个请求需要重新执行
let refreshReqArr: any[] = [];

//记录打开loading的数量，进入一次CallApi就加1
let loadingNum: number = 0;

//记录loading的实例
let loadingObj: any = null;

class AppBase {
    //#region 登录  Login
    /**
     * 登录
     * @param loginCode 系统编号
     * @param username 用户名
     * @param password 密码
     * @param options 参数
     * @param clientID 客户端标识ID
     * @param baseUrl Api主机地址
     */
    public static Login(loginCode: string, username: string, password: string, options?: any, clientID?: string, baseUrl?: string): void {
        let opts: any = {};
        MergeObjs(opts, options);

        let onSuccess = opts.success;
        let onError = opts.error;

        opts.success = (res: ResultModel) => {
            if (res.code == 0) {
                tokenStore().SetToken(res.data);
                onSuccess && onSuccess(res);
            } else {
                this.Tips({ msg: res.msg, type: TipsType.Error });
            }
        };

        opts.error = (res: ResultModel) => {
            onError && onError(res);
        };

        ApiCore.CallJwtToken(loginCode, username, password, opts, clientID, baseUrl);
    }

    //#endregion

    //#region 登出  Logout
    /**
     * 登出
     */
    public static Logout(): void {
        this.RemoveStoredData();
        this.Redirect();
    }
    //#endregion

    //#region 删除本地所有存储  RemoveStoredData
    /**
     * 删除本地所有存储【Cookie，Session，LocalSession】
     */
    public static RemoveStoredData(): void {
        Gx.ClearCookie();
        Gx.ClearSession();
        Gx.ClearLocalSession();
    }
    //#endregion

    //#region 重定向到新地址  Redirect
    /**
     * 重定向到新地址
     * @param url 转向地址
     */
    public static Redirect(url: string = "/login"): void {
        router.push({ path: url });
    }
    //#endregion

    //#region 得到当前Cookie中的Me的对象  GetMe
    /**
     * 得到当前Cookie中的Me的对象
     * @returns
     */
    public static GetMe(): any {
        return Gx.GetSession(Config._ME_KEY);
    }
    //#endregion

    //#region 得到当前Cookie中Token的对象  GetToken
    /**
     * 得到当前Cookie中Token的对象
     * @returns
     */
    public static GetToken(): any {
        return Gx.GetCookie(Config._TOKEN_KEY);
    }
    //#endregion

    //#region 调用Api刷新Me的信息并存储到Cookie和Store中  RefreshMe
    /**
     * 调用Api刷新Me的信息并存储到Cookie和Store中
     * @param opts
     */
    public static RefreshMe(opts?: any): Promise<any> {
        return this.CallApi(Api.Me, {
            success: (res: any) => {
                tokenStore().SetMe(res);
                opts && opts.success && opts.success(res);
            },
            fail: () => {
                this.Logout();
            },
            error: () => {
                this.Logout();
            },
        });
    }
    //#endregion

    //#region 验证是否已经登录  IsLogin
    /**
     * 验证是否已经登录，实际上就是验证本地Cookie中的Me和Token是否存在
     * @returns
     */
    public static IsLogin(): boolean {
        let token = this.GetToken();
        let me = this.GetMe();
        return !Gx.IsEmpty(token) && !Gx.IsEmpty(me);
    }
    //#endregion

    //#region 调用Api  CallApi
    /**
     * 调用Api
     * @param api ApiConnector中的对象
     * @param options 参数
     * @returns
     */
    public static CallApi(api: AjaxOption, options?: AjaxOption): Promise<any> {
        if (!api) {
            throw new Error("未提供有效的api信息！");
        }

        //定义ajax请求参数，给一些参数相应的默认值
        let opts: AjaxOption = {
            data: null,
            method: HttpType.GET,
            hasToken: true,
            loading: true,
            headers: {
                "Content-Type": "application/json",
            },
        };

        //合并参数
        MergeObjs(opts, api);
        MergeObjs(opts, options);

        //给请求加上时间戳
        if (_.includes(opts.url, "?")) {
            opts.url += "&t=" + new Date().getTime();
        } else {
            opts.url += "?t=" + new Date().getTime();
        }

        //处理loadingNum加1，并打开全局遮罩
        // if (++loadingNum === 1 && opts.loading == true) {
        //     loadingObj = ElLoading.service({
        //         lock: true,
        //         text: 'Loading',
        //         background: 'rgba(0, 0, 0, 0.7)',
        //     });
        // }

        //请求成功
        let onSuccess = opts.success;
        let onFail = opts.fail;
        let onException = opts.exception;
        opts.success = (res: ResultModel) => {
            switch (res.code) {
                case 0:
                    //api完全执行成功
                    onSuccess && onSuccess(res.data, res);
                    break;

                case 500:
                    //api执行报异常
                    if (onException) {
                        onException(res);
                    } else {
                        this.Tips({ msg: res.msg, type: TipsType.Error, time: 10000 });
                    }
                    break;

                default:
                    //api非完全执行成功
                    if (onFail) {
                        onFail(res);
                    } else {
                        this.Tips({ msg: res.msg });
                    }
                    break;
            }
        };

        //请求失败
        let onError = opts.error;
        opts.error = (xhr: any) => {
            let code = xhr.response.status;
            switch (code) {
                case 401:
                    if (!isRefreshToken) {
                        isRefreshToken = true;
                        refreshReqArr.push(opts);

                        //TODO:这里需要测试一下

                        //调用刷新token接口
                        ApiCore.CallOAuth2RefreshToken({
                            success: (res: TokenModel) => {
                                //把新的token存储到cookie中
                                tokenStore().SetToken(res);

                                //循环执行还未执行的请求
                                refreshReqArr.forEach((itemOpts: AjaxOption) => ApiCore.Call(itemOpts));
                                refreshReqArr = [];
                                isRefreshToken = false;
                            },
                            error: () => {
                                refreshReqArr = [];
                                isRefreshToken = false;
                                this.RemoveStoredData();

                                this.Alert({
                                    msg: "会话请求异常，请重新登录！",
                                    type: TipsType.Error,
                                    title: "出错啦！",
                                    onOk: () => {
                                        this.Logout();
                                    },
                                });
                            },
                        });
                    } else {
                        //正在刷新token，先记录下未完成的请求信息
                        refreshReqArr.push(opts);
                    }
                    break;

                default:
                    if (onError) {
                        onError(xhr);
                    } else {
                        this.Tips({ msg: "网络请求失败！", type: TipsType.Error });
                    }
                    break;
            }
        };

        //请求完成
        let onComplete = opts.complete;
        opts.complete = () => {
            // if (--loadingNum <= 0 && !Gx.IsEmpty(loadingObj)) {
            //     //请求完成时把num减1，并在num为0时关闭遮罩
            //     loadingObj.close();
            // }

            onComplete && onComplete();
        };

        return ApiCore.Call(opts);
    }
    //#endregion

    //#region 无按钮的提示信息  Tips
    /**
     * 无按钮的提示信息
     * @param options 参数
     */
    public static Tips(options: TipsOpts): void {
        let opts: any = {
            type: TipsType.Warning,
        };

        MergeObjs(opts, options);

        opts.message = opts.msg;
        opts.duration = opts.time;
        opts.type = opts.type;

        ElMessage(opts);
    }
    //#endregion

    //#region 有一个按钮的提示信息  Alert
    /**
     * 有一个按钮的提示信息
     * @param options 参数
     */
    public static Alert(options: TipsOpts): void {
        let opts: any = {
            type: TipsType.Info,
            title: "提示",
            okText: "确定",
            onOk: null,
        };

        MergeObjs(opts, options);
        opts.confirmButtonText = opts.okText;
        opts.type = opts.type;
        opts.callback = (res: string) => {
            if (res === "confirm") {
                opts.onOk && opts.onOk();
            }
        };

        ElMessageBox.alert(opts.msg, opts.title, opts);
    }
    //#endregion

    //#region 有二个按钮的提示信息  Confirm
    /**
     * 有二个按钮的提示信息
     * @param options
     */
    public static Confirm(options: TipsOpts): void {
        let opts: any = {
            type: TipsType.Info,
            title: "提示",
            okText: "确定",
            onOk: null,
            cancelText: "取消",
            onCancel: null,
        };

        MergeObjs(opts, options);
        opts.confirmButtonText = opts.okText;
        opts.cancelButtonText = opts.cancelText;
        opts.type = opts.type;
        opts.callback = (res: string) => {
            if (res === "confirm") {
                opts.onOk && opts.onOk();
            } else if (res === "cancel") {
                opts.onCancel && opts.onCancel();
            }
        };

        ElMessageBox.confirm(opts.msg, opts.title, opts);
    }
    //#endregion

    //#region 可输入的弹窗  prompt
    /**
     * 可输入的弹窗
     * @param options 参数
     */
    public static Prompt(options: TipsOpts): void {
        let opts: any = {
            type: TipsType.Info,
            title: "提示",
            okText: "确定",
            onOk: null,
            cancelText: "取消",
            onCancel: null,
            inputPattern: null,
            inputErrorMessage: null,
        };

        MergeObjs(opts, options);
        opts.confirmButtonText = opts.okText;
        opts.cancelButtonText = opts.cancelText;
        opts.type = opts.type;
        opts.callback = (res: any) => {
            if (res.action === "confirm") {
                opts.onOk && opts.onOk(res.value);
            } else if (res.action === "cancel") {
                opts.onCancel && opts.onCancel();
            }
        };

        ElMessageBox.prompt(opts.msg, opts.title, opts);
    }
    //#endregion

    //#region 上传

    /**
     * 获取七牛
     */
    public static GetQiniuInfo(): Promise<any> {
        return this.CallApi(Api.GetQiniuInfo, {
            success: (res) => {
                tokenStore().SetQiniuInfo(res);
            },
        });
    }

    /**
     * 上传
     * @param file 上传时选择的文件
     * @returns
     */
    public static Upload(file: File): Promise<any> {
        if (Config._UPLOAD_TYPE == UploadType.Qiniu) {
            return this.UploadQiniu(file);
        } else if (Config._UPLOAD_TYPE == UploadType.Local) {
            return this.UploadLocal(file);
        }
    }

    /**
     * 本地上传
     * @param file 上传时选择的文件
     */
    public static UploadLocal(file: File): Promise<any> {
        return new Promise((resolve, reject) => {
            let _res = null;

            var formData = new FormData();
            formData.append("file", file);

            let opts: AjaxOption = {
                url: Config._API_URL + "/Upload",
                method: HttpType.POST,
                data: formData,
                hasToken: true,
                success: function (res) {
                    _res = res;
                    resolve(_res);
                },
            };
            ApiCore.Call(opts);
        });
    }

    /**
     * 七牛上传
     * @param file 上传时选择的文件
     * @returns
     */
    public static UploadQiniu(file: File): Promise<any> {
        return new Promise((resolve, reject) => {
            let _res: any;

            let _qiniu = tokenStore().qiniu;

            let formData = new FormData();
            formData.append("file", file);
            formData.append("token", _qiniu.token);
            formData.append("domain", _qiniu.domain);
            formData.append("key", Gx.GenerateFileName(file));

            let opts: AjaxOption = {
                url: _qiniu.bucketArea,
                method: HttpType.POST,
                data: formData,
                success: (res: any) => {
                    _res = {};
                    _res.url = _qiniu.domain + "/" + res.key;
                    _res.fileName = file.name;
                    resolve(_res);
                },
                error: () => {
                    this.Tips({ msg: "上传失败！" });
                },
            };

            ApiCore.Call(opts);
        });
    }

    //#endregion
}

export default AppBase;
